//  byte_buffer.h
//
//  Copyright 2011 AzureSky <AzureSky.fedora@gmail.com>
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//  MA 02110-1301, USA.
//
//

#ifndef BYTE_BUFFER_H_
#define BYTE_BUFFER_H_

#include <vector>
#include <string>
#include <stdint.h>

namespace as
{

const std::size_t BUFFER_MAX_SIZE = 128 * 1024;

class byte_buffer
{
public:
    byte_buffer() :
        _read_index(0),
        _write_index(0),
        _storage(BUFFER_MAX_SIZE) {}

    ~byte_buffer() {}

    inline std::size_t get_size() const {
        return _write_index;
    }

    inline std::size_t get_unread_size() const {
        const int size = (_write_index - _read_index);
        if (size > 0)
            return size;
        else
            return 0;
    }

    inline std::size_t get_free_size() const {
        return (_storage.size() - get_size());
    }

    inline void reset_read_offset() {
        _read_index = 0;
    }

    inline char* get_data() {
        return &_storage[0];
    }

    template <typename T>
    void put(const T value) {
        const std::size_t size = sizeof(value);
        put(reinterpret_cast<const char*>(&value), size);
    }

    void put(const char* value, const std::size_t size) {
        if (get_free_size() < size)
            return;

        std::memcpy(&_storage.at(_write_index), value, size);
        _write_index += size;
    }

    template <typename T>
    T get() {
        T temp_type = (T)0;
        const std::size_t size = sizeof(temp_type);
        get(reinterpret_cast<char*>(&temp_type), size);
        return temp_type;
    }

    void get(char* value, const std::size_t size) {
        if (get_unread_size() >= size) {
            std::memcpy(value, &_storage.at(_read_index), size);
        }
        else {
            std::memset(value, 0, size);
        }

        _read_index += size;
    }

    /*
     * put signed function
     * */
    inline byte_buffer& operator<<(int8_t value) {
        put<int8_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(int16_t value) {
        put<int16_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(int32_t value) {
        put<int32_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(int64_t value) {
        put<int64_t>(value);
        return *this;
    }

    /*
     * put unsigned function
     * */
    inline byte_buffer& operator<<(uint8_t value) {
        put<uint8_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(uint16_t value) {
        put<uint16_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(uint32_t value) {
        put<uint32_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(uint64_t value) {
        put<uint64_t>(value);
        return *this;
    }

    inline byte_buffer& operator<<(bool value) {
        put<bool>(value);
        return *this;
    }

    inline byte_buffer& operator<<(float value) {
        put<float>(value);
        return *this;
    }

    inline byte_buffer& operator<<(double value) {
        put<double>(value);
        return *this;
    }

    inline byte_buffer& operator<<(const std::string& value) {
        put(value.c_str(), value.length());
        put<char>('\0');
        return *this;
    }

    inline byte_buffer& operator<<(const char* value) {
        put(value, std::strlen(value));
        put<char>('\0');
        return *this;
    }

    /*
     * get signed function
     * */
    inline byte_buffer& operator>>(int8_t& value) {
        value = get<int8_t>();
        return *this;
    }

    inline byte_buffer& operator>>(int16_t& value) {
        value = get<int16_t>();
        return *this;
    }

    inline byte_buffer& operator>>(int32_t& value) {
        value = get<int32_t>();
        return *this;
    }

    inline byte_buffer& operator>>(int64_t& value) {
        value = get<int64_t>();
        return *this;
    }

    /*
     * get unsigned function
     * */
    inline byte_buffer& operator>>(uint8_t& value) {
        value = get<uint8_t>();
        return *this;
    }

    inline byte_buffer& operator>>(uint16_t& value) {
        value = get<uint16_t>();
        return *this;
    }

    inline byte_buffer& operator>>(uint32_t& value) {
        value = get<uint32_t>();
        return *this;
    }

    inline byte_buffer& operator>>(uint64_t& value) {
        value = get<uint64_t>();
        return *this;
    }

    inline byte_buffer& operator>>(float& value) {
        value = get<float>();
        return *this;
    }

    inline byte_buffer& operator>>(double& value) {
        value = get<double>();
        return *this;
    }

    inline byte_buffer& operator>>(bool& value) {
        value = get<char>() > 0 ? true : false;
        return *this;
    }

    inline byte_buffer& operator>>(std::string& value) {
        value.clear();
        while (true) {
            char c = get<char>();
            if (c == '\0')
                break;
            value += c;
        }
        return *this;
    }

private:
    std::size_t _read_index;
    std::size_t _write_index;
    std::vector<char> _storage;

};

} // namespace as

#endif /* BYTE_BUFFER_H_ */
